package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.Member;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.enums.MemberType;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.helper.StringHelper;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.service.TeamService;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.List;

@Service
public class TeamServiceImpl implements TeamService {

    @Inject
    private UserRepository userRepository;

    @Inject
    private JdbcTemplate jdbcTemplate;

    @Override
    public Paging<Member> findUserMemberShip(MemberFindParam param) {
        User user = userRepository.findOne(param.getUserId());
        Specification<User> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            expressions.add(cb.like(root.get("relationPath"), user.getRelationPath() + "%"));
            expressions.add(cb.between(root.get("relationDepth"), user.getRelationDepth() + 1, user.getRelationDepth() + 3));
            if (param.getType() != null) {
                switch (param.getType()) {
                    case DIRECT:
                        expressions.add(cb.equal(root.get("superUser").get("id"), param.getUserId()));
                        break;
                    case INDIRECT:
                        expressions.add(cb.between(root.get("relationDepth"), user.getRelationDepth() + 2, user.getRelationDepth() + 3));
                        break;
                }
            }
            if (StringUtils.isNotBlank(param.getPhone())) {
                expressions.add(cb.equal(root.get("phone"), param.getPhone()));
            }
            expressions.add(cb.equal(root.get("deleted"), false));
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<User> page = userRepository.findAll(specification, pageable);

        return PagingHelper.of(page, (u) -> {
            Member m = new Member();
            m.setUserId(u.getId());
            m.setAvatarUrl(u.getAvatarUrl());
            m.setCity(u.getCity());
            m.setNickname(StringUtils.defaultIfEmpty(u.getPhone(), "")
                    .equals(u.getNickname()) ? StringHelper.protect(u.getNickname()) : u.getNickname());
            m.setCreateTime(u.getCreateTime());
            m.setType(u.getRelationDepth() == user.getRelationDepth() + 1 ? MemberType.DIRECT : MemberType.INDIRECT);
            m.setPhone(m.getType() == MemberType.DIRECT ? u.getPhone() : StringHelper.protect(u.getPhone()));
            m.setLevel(u.getLevel());
            return m;
        }, param.getPage(), param.getPageSize());
    }

    @Override
    public long findUserMemberShipNum(long userId) {
        User user = userRepository.findOne(userId);
        return userRepository.countByRelationPathStartsWith(user.getRelationPath());
    }

    @Override
    public MemberNumStat statUserMemberNum(long userId) {
        User user = userRepository.findOne(userId);
        String sql = "select count(1) as total, sum(case when u.relation_depth = ? then 1 else 0 end ) as direct, " +
                "sum(case when u.relation_depth between ? and ? then 1 else 0 end ) as indirect from user u " +
                " where u.relation_path like '" + user.getRelationPath() + "%' and u.relation_depth between ? and ? ";
        return jdbcTemplate.query(sql, new Object[]{user.getRelationDepth() + 1, user.getRelationDepth() + 2,
                user.getRelationDepth() + 3, user.getRelationDepth() + 1, user.getRelationDepth() + 3}, resultSet -> {
            resultSet.next();
            MemberNumStat stat = new MemberNumStat();
            stat.setTotal(resultSet.getLong("total"));
            stat.setIndirect(resultSet.getLong("indirect"));
            stat.setDirect(resultSet.getLong("direct"));
            resultSet.close();
            return stat;
        });
    }
}
